/*
JETware Hands-free Extension provides additional features for Windows Mobile HFP support.
Copyright (C) 2004-2009 Jim Thatcher

This program is free software; you can redistribute it and/or modify it under the terms of the 
GNU General Public License as published by the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; 
if not, write to the Free Software Foundation, Inc., 
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Source Control header

$Workfile: NotifyMgr.cpp $
$Revision: 2 $
$Date: 3/24/07 11:56p $
*/
#include "stdafx.h"
#include "NotifyMgr.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef HRESULT WINAPI _RegistryNotifyMsgQueue(HKEY hKey, LPCTSTR pszSubKey, LPCTSTR pszValueName, LPCTSTR pszMsgQueue, DWORD dwUserData, NOTIFICATIONCONDITION * pCondition, HREGNOTIFY * phNotify);
typedef HRESULT WINAPI _RegistryCloseNotification(HREGNOTIFY hNotify);

_RegistryNotifyMsgQueue * RtRegistryNotifyMsgQueue = NULL;
_RegistryCloseNotification * RtRegistryCloseNotification = NULL;

BOOL IsNotifyAvailable()
{
	BOOL bRet = FALSE;
	HINSTANCE hDll = LoadLibrary(_T("aygshell.dll"));  // library is freed in CNotifyMgr dtor
	if (hDll != NULL)
	{
		RtRegistryNotifyMsgQueue = (_RegistryNotifyMsgQueue *) GetProcAddress(hDll, _T("RegistryNotifyMsgQueue"));
		RtRegistryCloseNotification = (_RegistryCloseNotification *) GetProcAddress(hDll, _T("RegistryCloseNotification"));
	}
	if ((RtRegistryNotifyMsgQueue != NULL) && (RtRegistryCloseNotification != NULL))
		bRet = TRUE;
#ifdef DEBUG
	{
		CHAR szLog[256];
		sprintf(szLog, "LoadLibrary - %8.8X, RegNotifyMsgQ=%8.8X, RegCloseNotify=%8.8X", hDll, RtRegistryNotifyMsgQueue, RtRegistryCloseNotification);
		LogString(szLog, DEBUG_NOTIFY);
	}
#endif
	return bRet;
}

#ifdef __cplusplus
}
#endif

DWORD GetCurrentDWORD(HKEY hKeyRoot, LPCTSTR pszSubKey, LPCTSTR pszValueName, DWORD dwMask)
{
	HKEY	hKey				= NULL;
	DWORD   dwType              = 0;
	DWORD	dwSize				= 0;
	LONG	lRet				= 0;
	DWORD	dwValue				= 0;
	DWORD	dwResult			= 0;
#ifdef DEBUG
	CHAR szLog[64];
#endif
	lRet = RegOpenKeyEx(hKeyRoot, pszSubKey, 0, 0, &hKey);
	if (ERROR_SUCCESS == lRet)
	{
		dwSize = sizeof(dwValue);
		dwType = 0;
		lRet = RegQueryValueEx(hKey, pszValueName, NULL, &dwType, (LPBYTE) &dwValue, &dwSize);
		if (ERROR_SUCCESS == lRet)
		{
			if (dwSize && (dwType == REG_DWORD))
			{
				dwResult = dwValue & dwMask;
#ifdef DEBUG
				sprintf(szLog, "Read %S value = %i (%i)", pszValueName, dwResult, dwValue);
				LogString(szLog, DEBUG_NOTIFY);
#endif
			}
		}
		RegCloseKey(hKey);
	}
	return dwResult;
}

BOOL GetCurrentString(HKEY hKeyRoot, LPCTSTR pszSubKey, LPCTSTR pszValueName, LPTSTR *pszValue)
{
	HKEY	hKey				= NULL;
	DWORD   dwType              = 0;
	DWORD	dwSize				= 0;
	LONG	lRet				= 0;
	TCHAR	szValue[MAX_PATH];
	BOOL	bResult				= FALSE;
#ifdef DEBUG
	CHAR szLog[64];
#endif
	lRet = RegOpenKeyEx(hKeyRoot, pszSubKey, 0, 0, &hKey);
	if (ERROR_SUCCESS == lRet)
	{
		dwSize = sizeof(szValue);
		dwType = 0;
		lRet = RegQueryValueEx(hKey, pszValueName, NULL, &dwType, (LPBYTE) &szValue, &dwSize);
		if (ERROR_SUCCESS == lRet)
		{
			if (dwSize && (dwType == REG_SZ))
			{
				if (*pszValue != NULL)
				{
					LocalFree(*pszValue);
				}
				*pszValue = (LPTSTR) LocalAlloc(LPTR, dwSize);
				if (*pszValue != NULL)
				{
					memcpy(*pszValue, &szValue, dwSize);
					bResult = TRUE;
#ifdef DEBUG
					sprintf(szLog, "Read %S value = %S", pszValueName, *pszValue);
					LogString(szLog, DEBUG_NOTIFY);
#endif
				}
			}
		}
		RegCloseKey(hKey);
	}
	return bResult;
}

CNotifyMgr::CNotifyMgr(HANDLE * phMsgQueue)
{
#ifdef DEBUG
	CHAR szLog[256];
#endif
	DWORD dwNotifyMsgSize = sizeof(NOTIFYMSGQUEUEPACKET) + (MAX_PATH * sizeof(TCHAR));
	m_bMailEnabled[MESSAGES_SM] = FALSE;
	m_bMailEnabled[MESSAGES_ME] = FALSE;
	m_dwSignal = 0;
	m_szOperator = NULL;
#ifdef USE_NOTIFY_CID
	m_szCallerName = NULL;
	m_szCallerNumber = NULL;
	m_wCallIDActive = 0;
#endif
	m_wCallCount = 0;
	m_dwPhoneStatus = 0;
	m_dwBTStatus = 0;
#ifdef USE_NOTIFY_HF
	m_bHandsfree = FALSE;
#endif
	m_hMsgQueue = NULL;
	for (int n=0; n<MAX_NOTIFY_TYPE; n++)
	{
		m_NotifyHandles[n] = NULL;
	}

	// Set up Message Queue
	if (*phMsgQueue == NULL)
	{
		MSGQUEUEOPTIONS qOptions;
		qOptions.dwSize = sizeof(MSGQUEUEOPTIONS); 
		qOptions.dwFlags = MSGQUEUE_NOPRECOMMIT;
		qOptions.bReadAccess = TRUE;
		qOptions.cbMaxMessage = dwNotifyMsgSize;
		qOptions.dwMaxMessages = 10;
		m_hMsgQueue = CreateMsgQueue(JET_NOTIFY_QUEUE_NAME, &qOptions);
#ifdef DEBUG
		sprintf(szLog, "CreateMsgQ for notify = %8.8X, Error=%i", m_hMsgQueue, GetLastError());
		LogString(szLog, DEBUG_NOTIFY);
#endif
	}
	if (m_hMsgQueue != NULL)
	{
		// Register for notifications for BTStatus
		HRESULT hr = 0;
		NOTIFICATIONCONDITION ncBTStatus;
		ncBTStatus.ctComparisonType = REG_CT_ANYCHANGE;
		ncBTStatus.dwMask = SN_BLUETOOTH_ALL_BITMASK;
		ncBTStatus.TargetValue.dw = SN_BLUETOOTH_ALL_BITMASK;
		hr = RtRegistryNotifyMsgQueue(SN_BLUETOOTH_ROOT, SN_BLUETOOTH_PATH, SN_BLUETOOTH_VALUE,
									JET_NOTIFY_QUEUE_NAME, BT_STATUS, &ncBTStatus, &m_NotifyHandles[BT_STATUS]);
		m_dwBTStatus = GetCurrentDWORD(SN_BLUETOOTH_ROOT, SN_BLUETOOTH_PATH, SN_BLUETOOTH_VALUE, /*SN_BLUETOOTH_ALL_BITMASK*/ 0xFFFF);
#ifdef USE_NOTIFY_HF
		ncBTStatus.dwMask = 0xFF;
		hr = RtRegistryNotifyMsgQueue(SN_HEADSETPRESENT_ROOT, SN_HEADSETPRESENT_PATH, SN_HANDSFREE_VALUE,
									JET_NOTIFY_QUEUE_NAME, HANDS_FREE, &ncBTStatus, &m_NotifyHandles[HANDS_FREE]);
		m_bHandsfree = GetCurrentDWORD(SN_HEADSETPRESENT_ROOT, SN_HEADSETPRESENT_PATH, SN_HANDSFREE_VALUE, 0xFF);
#endif
#ifdef DEBUG
		sprintf(szLog, "CNotifyMgr ctor RegNotify returned %8.8X, BTStatus=%i", hr, m_dwBTStatus);
		LogString(szLog, DEBUG_NOTIFY);
#endif
	}
	*phMsgQueue = m_hMsgQueue;
}

CNotifyMgr::~CNotifyMgr()
{
	//Stop all notifications
	for (int n=0; n < MAX_NOTIFY_TYPE; n++)
	{
		if (m_NotifyHandles[n] != NULL)
			RtRegistryCloseNotification(m_NotifyHandles[n]);
	}
	//Close message queue
	if (m_hMsgQueue != NULL)
	{
		CloseHandle(m_hMsgQueue);
		m_hMsgQueue = NULL;
	}
	HINSTANCE hDll = GetModuleHandle(_T("aygshell.dll"));
	if (hDll != NULL)
	{
		FreeLibrary(hDll);
		RtRegistryNotifyMsgQueue = NULL;
		RtRegistryCloseNotification = NULL;
	}
}

BOOL CNotifyMgr::StartIndicators()
{
	BOOL bRet = FALSE;
	if (m_hMsgQueue != NULL)
	{
		// Register for notifications
		HRESULT hr = 0;
		NOTIFICATIONCONDITION nc;

		nc.ctComparisonType = REG_CT_ANYCHANGE;
		if (m_NotifyHandles[SIGNAL_LEVEL] == NULL)
		{
			nc.dwMask = 0xFF;
			nc.TargetValue.dw = 0xFF;
			hr = RtRegistryNotifyMsgQueue(SN_PHONESIGNALSTRENGTH_ROOT, SN_PHONESIGNALSTRENGTH_PATH, SN_PHONESIGNALSTRENGTH_VALUE,
										JET_NOTIFY_QUEUE_NAME, SIGNAL_LEVEL, &nc, &m_NotifyHandles[SIGNAL_LEVEL]);
		}

		if (m_NotifyHandles[PHONE_STATUS] == NULL)
		{
			nc.dwMask = SN_PHONE_STATUS_ALL_BITMASK;
			nc.TargetValue.dw = SN_PHONE_STATUS_ALL_BITMASK;
			hr = RtRegistryNotifyMsgQueue(SN_PHONECALLTALKING_ROOT, SN_PHONECALLTALKING_PATH, SN_PHONECALLTALKING_VALUE,
										JET_NOTIFY_QUEUE_NAME, PHONE_STATUS, &nc, &m_NotifyHandles[PHONE_STATUS]);
		}

		if (m_NotifyHandles[CALL_COUNT] == NULL)
		{
			nc.dwMask = 0xFF;
			nc.TargetValue.dw = 0xFF;
			hr = RtRegistryNotifyMsgQueue(SN_PHONEACTIVECALLCOUNT_ROOT, SN_PHONEACTIVECALLCOUNT_PATH, SN_PHONEACTIVECALLCOUNT_VALUE,
										JET_NOTIFY_QUEUE_NAME, CALL_COUNT, &nc, &m_NotifyHandles[CALL_COUNT]);
		}
		if (m_NotifyHandles[OPERATOR_NAME] == NULL)
		{
			hr = RtRegistryNotifyMsgQueue(SN_PHONEOPERATORNAME_ROOT, SN_PHONEOPERATORNAME_PATH, SN_PHONEOPERATORNAME_VALUE,
										JET_NOTIFY_QUEUE_NAME, OPERATOR_NAME, NULL, &m_NotifyHandles[OPERATOR_NAME]);
		}
		bRet = TRUE;
	}
	// Read current values of everything
	m_dwSignal = GetCurrentDWORD(SN_PHONESIGNALSTRENGTH_ROOT, SN_PHONESIGNALSTRENGTH_PATH, SN_PHONESIGNALSTRENGTH_VALUE, 0xFF);
	m_dwPhoneStatus = GetCurrentDWORD(SN_PHONECALLTALKING_ROOT, SN_PHONECALLTALKING_PATH, SN_PHONECALLTALKING_VALUE, /*SN_PHONE_STATUS_ALL_BITMASK*/ 0xFFFFFFFF);
	m_wCallCount = (WORD) GetCurrentDWORD(SN_PHONEACTIVECALLCOUNT_ROOT, SN_PHONEACTIVECALLCOUNT_PATH, SN_PHONEACTIVECALLCOUNT_VALUE, 0xFF);
	GetCurrentString(SN_PHONEOPERATORNAME_ROOT, SN_PHONEOPERATORNAME_PATH, SN_PHONEOPERATORNAME_VALUE, &m_szOperator);
	// Set value of global variables used in reporting
	g_dwSignalLevel = m_dwSignal * 0x28F; // convert from 100 scale to 0xFFFF scale
	g_bRoaming = Roaming();
	g_bInCall = InCall();
	g_bService = HasService();
	g_dwCallSetup = CallSetup();
	g_dwNumVoiceCalls = CallCount();

	return (bRet);
}

void CNotifyMgr::StopIndicators()
{
	if (m_NotifyHandles[SIGNAL_LEVEL] != NULL)
	{
		RtRegistryCloseNotification(m_NotifyHandles[SIGNAL_LEVEL]);
		m_NotifyHandles[SIGNAL_LEVEL] = NULL;
	}
	if (m_NotifyHandles[PHONE_STATUS] != NULL)
	{
		RtRegistryCloseNotification(m_NotifyHandles[PHONE_STATUS]);
		m_NotifyHandles[PHONE_STATUS] = NULL;
	}
	if (m_NotifyHandles[CALL_COUNT] != NULL)
	{
		RtRegistryCloseNotification(m_NotifyHandles[CALL_COUNT]);
		m_NotifyHandles[CALL_COUNT] = NULL;
	}
	if (m_NotifyHandles[OPERATOR_NAME] != NULL)
	{
		RtRegistryCloseNotification(m_NotifyHandles[OPERATOR_NAME]);
		m_NotifyHandles[OPERATOR_NAME] = NULL;
	}
}

BOOL CNotifyMgr::StartMail(DWORD dwMsgStore)
{
	BOOL bRet = FALSE;
	if ((m_hMsgQueue != NULL) &&  (dwMsgStore <= MESSAGES_SM))
	{
		// Register for notifications
		HRESULT hr = 0;
		NOTIFICATIONCONDITION nc;
		nc.ctComparisonType = REG_CT_ANYCHANGE;
		if (m_NotifyHandles[UNREAD_MAIL] == NULL)
		{
			nc.dwMask = 0xFFFF;
			nc.TargetValue.dw = 0xFFFF;
			hr = RtRegistryNotifyMsgQueue(SN_MESSAGINGTOTALEMAILUNREAD_ROOT, SN_MESSAGINGTOTALEMAILUNREAD_PATH, SN_MESSAGINGTOTALEMAILUNREAD_VALUE,
										JET_NOTIFY_QUEUE_NAME, UNREAD_MAIL, &nc, &m_NotifyHandles[UNREAD_MAIL]);
		}
		bRet = TRUE;
		m_bMailEnabled[dwMsgStore] = TRUE;
	}
	return (bRet);
}

BOOL CNotifyMgr::StopMail(DWORD dwMsgStore)
{
	if (dwMsgStore <= MESSAGES_SM)
	{
		m_bMailEnabled[dwMsgStore] = FALSE;
		if (!(m_bMailEnabled[MESSAGES_ME]) && !(m_bMailEnabled[MESSAGES_SM]))
		{
			if (m_NotifyHandles[UNREAD_MAIL] != NULL)
			{
				RtRegistryCloseNotification(m_NotifyHandles[UNREAD_MAIL]);
				m_NotifyHandles[UNREAD_MAIL] = NULL;
			}
		}
	}
	return TRUE;
}

#ifdef USE_NOTIFY_CID
BOOL CNotifyMgr::StartCallerID(WORD wIDType)
{
	BOOL bRet = FALSE;
	if (m_hMsgQueue != NULL)
	{
		// Register for notifications
		HRESULT hr = 0;
		m_wCallIDActive |= wIDType;
		if (m_NotifyHandles[CALLER_NAME] == NULL)
		{
			hr = RtRegistryNotifyMsgQueue(SN_PHONEINCOMINGCALLERNAME_ROOT, SN_PHONEINCOMINGCALLERNAME_PATH, SN_PHONEINCOMINGCALLERNAME_VALUE,
										JET_NOTIFY_QUEUE_NAME, CALLER_NAME, NULL, &m_NotifyHandles[CALLER_NAME]);
		}
		if (m_NotifyHandles[TALKING_NAME] == NULL)
		{
			hr = RtRegistryNotifyMsgQueue(SN_PHONETALKINGCALLERNAME_ROOT, SN_PHONETALKINGCALLERNAME_PATH, SN_PHONETALKINGCALLERNAME_VALUE,
										JET_NOTIFY_QUEUE_NAME, TALKING_NAME, NULL, &m_NotifyHandles[TALKING_NAME]);
		}
		if (m_wCallIDActive & JET_REPORT_CLIP)
		{
			if (m_NotifyHandles[CALLER_NUMBER] == NULL)
			{
				hr = RtRegistryNotifyMsgQueue(SN_PHONEINCOMINGCALLERNUMBER_ROOT, SN_PHONEINCOMINGCALLERNUMBER_PATH, SN_PHONEINCOMINGCALLERNUMBER_VALUE,
											JET_NOTIFY_QUEUE_NAME, CALLER_NUMBER, NULL, &m_NotifyHandles[CALLER_NUMBER]);
			}
			if (m_NotifyHandles[TALKING_NUMBER] == NULL)
			{
				hr = RtRegistryNotifyMsgQueue(SN_PHONETALKINGCALLERNUMBER_ROOT, SN_PHONETALKINGCALLERNUMBER_PATH, SN_PHONETALKINGCALLERNUMBER_VALUE,
											JET_NOTIFY_QUEUE_NAME, TALKING_NUMBER, NULL, &m_NotifyHandles[TALKING_NUMBER]);
			}
		}
		bRet = TRUE;
	}
	return (bRet);
}

BOOL CNotifyMgr::StopCallerID(WORD wIDType)
{
	// TODO: Clear CLIP or ELIP flag
	if (m_wCallIDActive & wIDType)
		m_wCallIDActive ^= wIDType;
	if (!(m_wCallIDActive & JET_REPORT_CLIP))
	{
		if (m_NotifyHandles[CALLER_NUMBER] != NULL)
		{
			RtRegistryCloseNotification(m_NotifyHandles[CALLER_NUMBER]);
			m_NotifyHandles[CALLER_NUMBER] = NULL;
		}
		if (m_NotifyHandles[TALKING_NUMBER] != NULL)
		{
			RtRegistryCloseNotification(m_NotifyHandles[TALKING_NUMBER]);
			m_NotifyHandles[TALKING_NUMBER] = NULL;
		}
	}
	if (!m_wCallIDActive)
	{
		if (m_NotifyHandles[CALLER_NAME] != NULL)
		{
			RtRegistryCloseNotification(m_NotifyHandles[CALLER_NAME]);
			m_NotifyHandles[CALLER_NAME] = NULL;
		}
		if (m_NotifyHandles[TALKING_NAME] != NULL)
		{
			RtRegistryCloseNotification(m_NotifyHandles[TALKING_NAME]);
			m_NotifyHandles[TALKING_NAME] = NULL;
		}
	}
	return TRUE;
}

#endif
BOOL CNotifyMgr::ProcessMessages()
{
	DWORD cbRead = 0;
	DWORD dwFlags = 0;
	DWORD dwNotifyMsgSize = sizeof(NOTIFYMSGQUEUEPACKET) + (MAX_PATH * sizeof(TCHAR));

	NOTIFYMSGQUEUEPACKET *pNotify = (NOTIFYMSGQUEUEPACKET*) new BYTE[dwNotifyMsgSize];
#ifdef DEBUG
	CHAR szLog[128];
#endif        
	while (ReadMsgQueue(m_hMsgQueue, pNotify, dwNotifyMsgSize, &cbRead, 0, &dwFlags))
	{
#ifdef DEBUG
		sprintf(szLog, "Notify message %i with %i bytes, value %8.8X", pNotify->dwUserData, pNotify->cbData, *(DWORD *)(pNotify->rgData));
		LogString(szLog, DEBUG_NOTIFY);
#endif
		switch (pNotify->dwUserData)
		{
			case PHONE_STATUS:
			{
				if (pNotify->cbData >= sizeof(DWORD))
				{
					DWORD dwChangedData = (*(DWORD *) (pNotify->rgData)) ^ m_dwPhoneStatus;
					m_dwPhoneStatus = *(DWORD *) (pNotify->rgData);
#ifdef DEBUG
					sprintf(szLog, "PHONE_STATUS changed %8.8X to %8.8X", dwChangedData, m_dwPhoneStatus);
					LogString(szLog, DEBUG_NOTIFY);
#endif
					if (dwChangedData & SN_PHONEROAMING_BITMASK)
					{
						g_bRoaming = Roaming();
						if (HFPConnected() && g_bReportStatus)
						{
							CHAR szReply[20];
							sprintf(szReply, "+CIEV: 6,%i", Roaming());
							PostResponse(szReply);
						}
					}
					if (dwChangedData & SN_PHONENOSERVICE_BITMASK)
					{
						g_bService = HasService();
						if (HFPConnected() && g_bReportStatus && g_bNeedExtraStatus)
						{
							CHAR szReply[20];
							sprintf(szReply, "+CIEV: 1,%i", HasService());
							PostResponse(szReply);
						}
					}
#ifdef USE_NOTIFY_TAPI
					if (dwChangedData & SN_PHONEINCOMINGCALL_BITMASK)
					{
						g_dwCallSetup = CALLSETUP_INCOMING;
						ReportCallSetup();
						if (g_dwInBandRing)
						{
#ifdef DEBUG
							LogString("Incoming call - Opening audio", DEBUG_INBANDRING);
#endif
							ConnectAudio();
						}
					}
					if (dwChangedData & SN_PHONECALLCALLING_BITMASK)
					{
						g_dwCallSetup = CALLSETUP_OUTRING;
						ReportCallSetup();
					}
					if ((dwChangedData & SN_PHONECALLTALKING_BITMASK) && HFPConnected())
					{
						g_dwCallSetup = CALLSETUP_NONE;
						g_bAnsweringCall = FALSE; // TODO: Track down need for this
						if (g_bReportStatus && g_bNeedExtraStatus)
						{
							CHAR szReply[20];
							sprintf(szReply, "+CIEV: 2,%i", InCall());
							PostResponse(szReply);
							ReportCallSetup();
						}
						if (InCall())
						{
							if (g_dwFeatureMask & FM_RESET_AUDIO)
							{
#ifdef DEBUG
								LogString("Incoming call connected - Reconnecting audio", DEBUG_INBANDRING);
#endif
								DisconnectAudio();
								ConnectAudio();
							}
						}
						else
						{
							if (g_dwSleepOnDisconnect)
							{
#ifdef DEBUG
								LogString("Setting power to unattended", DEBUG_INFO);
#endif
								SuspendDevice();
							}
						}
					}
#endif
				}
				break;
			}
			case BT_STATUS:
			{
				if (pNotify->cbData >= sizeof(DWORD))
				{
					DWORD dwChangedData = (*(DWORD *) (pNotify->rgData)) ^ m_dwBTStatus;
					m_dwBTStatus = *(DWORD *) (pNotify->rgData);
#ifdef DEBUG
					sprintf(szLog, "BT_STATUS changed %8.8X to %8.8X", dwChangedData, m_dwBTStatus);
					LogString(szLog, DEBUG_NOTIFY);
#endif
					if (dwChangedData & SN_BLUETOOTH_HFP_CONTROL_BITMASK)
					{
						// It's not clear there is an advantage to tracking connection this way over the thread handle approach
						/*
						if (HFPConnected())
						{
							// New session started
							InitializeSession(TRUE);
						}
						else
						{
							// Clean up session
						}
						*/
					}
					if (dwChangedData & SN_BLUETOOTH_HFP_AUDIO_BITMASK)
					{
						// Reconnect audio
						if ((g_dwFeatureMask & FM_KEEP_AUDIO_ON) && HFPConnected())
						{
#ifdef DEBUG
							LogString("Reconnecting audio", DEBUG_INFO);
#endif
							ConnectAudio();
						}
					}
				}
				break;
			}
			case UNREAD_MAIL:
			{
				if ((pNotify->cbData >= sizeof(DWORD)) && HFPConnected())
				{
					if (MailNotifyEnabled(MESSAGES_ME) || MailNotifyEnabled(MESSAGES_SM))
						CheckForNewMessages();
				}
				break;
			}
			case SIGNAL_LEVEL:
			{
				DWORD dwChangedData = 0;
				if (pNotify->cbData >= sizeof(DWORD))
					dwChangedData = *(DWORD *) pNotify->rgData;
				else
					dwChangedData = 0;
				DWORD dwCurrSignal = (dwChangedData < 90) ? ((dwChangedData + 10) / 20) : 5;
				DWORD dwLastReportedSignal = (m_dwSignal < 90) ? ((m_dwSignal + 10) / 20) : 5;
				m_dwSignal = dwChangedData;
				g_dwSignalLevel = m_dwSignal * 0x28F; // Convert from 100 based scale to 0xFFFF based scale
#ifdef DEBUG
				sprintf(szLog, "Signal raw = %i, level = %i, service = %i", m_dwSignal, dwCurrSignal, HasService());
				LogString(szLog, DEBUG_TAPI);
#endif
				// If we have no service, don't trust signal level
				if ((m_dwSignal > 0) && !HasService())
				{
					m_dwSignal = 0;
					dwCurrSignal = 0;
				}
				if (dwCurrSignal != dwLastReportedSignal)
				{
					if (g_bReportStatus)
					{
						CHAR szReply[20];
						sprintf(szReply, "+CIEV: 5,%i", dwCurrSignal);
						PostResponse(szReply);
					}
				}
				break;
			}
			case OPERATOR_NAME:
			{
				BOOL bChanged = FALSE;
				if (m_szOperator != NULL)
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						if (wcsncmp(m_szOperator, (TCHAR *) pNotify->rgData, pNotify->cbData/sizeof(TCHAR)) != 0)
						{
							m_szOperator = (TCHAR *) LocalReAlloc(m_szOperator, pNotify->cbData, LPTR);
							bChanged = TRUE;
						}
					}
					else
					{
						LocalFree(m_szOperator);
						m_szOperator = NULL;
						bChanged = TRUE;
					}
				}
				else
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						m_szOperator = (TCHAR *) LocalAlloc(LPTR, pNotify->cbData);
						bChanged = TRUE;
					}
				}
				if (m_szOperator != NULL && bChanged)
				{
					memcpy(m_szOperator, pNotify->rgData, pNotify->cbData);
				}
#ifdef DEBUG
				sprintf(szLog, "Notified of operator change (%i) to %S", bChanged, m_szOperator);
				LogString(szLog, DEBUG_INDICATORS);
#endif
				if (bChanged)
				{
					if (g_bReportStatus)
					{
						CHAR szReply[20];
						// To support Operator info, send +CIEV: (roam),x always
						// to cover case when switching from one roam operator to another
						sprintf(szReply, "+CIEV: 6,%i", Roaming());
						PostResponse(szReply);
					}
				}
				break;
			}
			case CALL_COUNT:
			{
				if (pNotify->cbData >= sizeof(DWORD))
				{
					m_wCallCount = (WORD) (*(DWORD *) pNotify->rgData);
					// Notifications not required, we just track this to respond to +CLCC requests
				}
				else if (pNotify->cbData == 0)
					m_wCallCount = 0;
				break;
			}
#ifdef USE_NOTIFY_CID
			case CALLER_NAME:
			{
				BOOL bChanged = FALSE;
				if (m_szCallerName != NULL)
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						if (wcsncmp(m_szCallerName, (TCHAR *) pNotify->rgData, pNotify->cbData/sizeof(TCHAR)) != 0)
						{
							m_szCallerName = (TCHAR *) LocalReAlloc(m_szCallerName, pNotify->cbData, LPTR);
							bChanged = TRUE;
						}
					}
					else
					{
						LocalFree(m_szCallerName);
						m_szCallerName = NULL;
						bChanged = TRUE;
					}
				}
				else
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						m_szCallerName = (TCHAR *) LocalAlloc(LPTR, pNotify->cbData);
						bChanged = TRUE;
					}
				}
				if (m_szCallerName != NULL && bChanged)
				{
					memcpy(m_szCallerName, pNotify->rgData, pNotify->cbData);
#ifdef DEBUG
					sprintf(szLog, "Notified of CallerName change (%i) to %S", bChanged, m_szCallerName);
					LogString(szLog, DEBUG_CALLERID);
#endif
					if (m_wCallIDActive) //either CLIP or ELIP
					{
						DWORD	dwType = 129;
						CHAR	szReply[1024];
						TCHAR	szNumber[32];
						BOOL	bReadNumber = FALSE;
						CHAR	szMappedName[512];
						szMappedName[0] = 0;

						if (m_szCallerNumber == NULL)
							bReadNumber = GetCurrentString(SN_PHONEINCOMINGCALLERNUMBER_ROOT, SN_PHONEINCOMINGCALLERNUMBER_PATH, SN_PHONEINCOMINGCALLERNUMBER_VALUE, &m_szCallerNumber);
						else
						{
							bReadNumber = TRUE;
						}
#ifdef DEBUG
						sprintf(szLog, "Caller ID number is %ls, bReadNumber = %i", m_szCallerNumber, bReadNumber);
						LogString(szLog, DEBUG_CALLERID);
#endif
						
						if (m_szCallerNumber != NULL)
						{
							if (*m_szCallerNumber == '+')
								dwType = 145;
							else if (*m_szCallerNumber == '1')
								dwType = 161;
						}
						if (*m_szCallerName != 0)
							ConvertString(szMappedName, m_szCallerName, sizeof(szMappedName));
						PostResponse("\r\nRING");
						if (ReportELIP())
						{
							if (szMappedName[0] != 0)
								sprintf(szReply, "*ELIP: \"%s\"", szMappedName);
							PostResponse(szReply);
						}
						if (ReportCLIP() && bReadNumber && (m_szCallerNumber != NULL))
						{
							StripPhoneNumber(m_szCallerNumber, szNumber, 32);
							if ((g_dwReportCIDName) && (szMappedName[0] != 0)) // Put caller name where number should be - HACK for Nokia HS-12W
								sprintf(szReply, "+CLIP: \"%s\",%i", szMappedName, dwType);
							else if (szMappedName[0] != 0)
								sprintf(szReply, "+CLIP: \"%ls\",%i,,,\"%s\"", szNumber, dwType, szMappedName);
							else
								sprintf(szReply, "+CLIP: \"%ls\",%i", szNumber, dwType);
							PostResponse(szReply);
						}
					}
				}
				break;
			}
			case CALLER_NUMBER:
			{
				BOOL bChanged = FALSE;
				if (m_szCallerNumber != NULL)
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						if (wcsncmp(m_szCallerNumber, (TCHAR *) pNotify->rgData, pNotify->cbData/sizeof(TCHAR)) != 0)
						{
							m_szCallerNumber = (TCHAR *) LocalReAlloc(m_szCallerNumber, pNotify->cbData, LPTR);
							bChanged = TRUE;
						}
					}
					else
					{
						LocalFree(m_szCallerNumber);
						m_szCallerNumber = NULL;
						bChanged = TRUE;
					}
				}
				else
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						m_szCallerNumber = (TCHAR *) LocalAlloc(LPTR, pNotify->cbData);
						bChanged = TRUE;
					}
				}
				if (m_szCallerNumber != NULL && bChanged)
				{
					memcpy(m_szCallerNumber, pNotify->rgData, pNotify->cbData);
#ifdef DEBUG
					sprintf(szLog, "Notified of CallerNumber change (%i) to %S", bChanged, m_szCallerNumber);
					LogString(szLog, DEBUG_CALLERID);
#endif
					if (m_wCallIDActive) //either CLIP or ELIP
					{
						DWORD	dwType = 129;
						CHAR	szReply[1024];
						TCHAR	szNumber[32];
						CHAR	szMappedName[512];
						szMappedName[0] = 0;
						
						if (m_szCallerNumber != NULL)
						{
							if (*m_szCallerNumber == '+')
								dwType = 145;
							else if (*m_szCallerNumber == '1')
								dwType = 161;
						}

						if (m_szCallerName == NULL)
							GetCurrentString(SN_PHONEINCOMINGCALLERNAME_ROOT, SN_PHONEINCOMINGCALLERNAME_PATH, SN_PHONEINCOMINGCALLERNAME_VALUE, &m_szCallerName);
						if (*m_szCallerName != 0)
							ConvertString(szMappedName, m_szCallerName, sizeof(szMappedName));
						PostResponse("\r\nRING");
						if (ReportELIP())
						{
							if (szMappedName[0] != 0)
								sprintf(szReply, "*ELIP: \"%s\"", szMappedName);
							PostResponse(szReply);
						}
						if (ReportCLIP())
						{
							StripPhoneNumber(m_szCallerNumber, szNumber, 32);
							if ((g_dwReportCIDName) && (szMappedName[0] != 0)) // Put caller name where number should be - HACK for Nokia HS-12W
								sprintf(szReply, "+CLIP: \"%s\",%i", szMappedName, dwType);
							else if (szMappedName[0] != 0)
								sprintf(szReply, "+CLIP: \"%ls\",%i,,,\"%s\"", szNumber, dwType, szMappedName);
							else
								sprintf(szReply, "+CLIP: \"%ls\",%i", szNumber, dwType);
							PostResponse(szReply);
						}
					}
				}
				break;
			}
			case TALKING_NAME:
			{
				BOOL bChanged = FALSE;
				if (m_szCallerName != NULL)
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						if (wcsncmp(m_szCallerName, (TCHAR *) pNotify->rgData, pNotify->cbData/sizeof(TCHAR)) != 0)
						{
							m_szCallerName = (TCHAR *) LocalReAlloc(m_szCallerName, pNotify->cbData, LPTR);
							bChanged = TRUE;
						}
					}
					else
					{
						LocalFree(m_szCallerName);
						m_szCallerName = NULL;
						bChanged = TRUE;
					}
				}
				else
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						m_szCallerName = (TCHAR *) LocalAlloc(LPTR, pNotify->cbData);
						bChanged = TRUE;
					}
				}
				if (m_szCallerName != NULL && bChanged)
				{
					memcpy(m_szCallerName, pNotify->rgData, pNotify->cbData);
					/*
					if (m_wCallIDActive) //either CLIP or ELIP
					{
						DWORD	dwType = 129;
						CHAR	szReply[1024];
						CHAR	szMappedName[512];
						szMappedName[0] = 0;
						
						if (m_szCallerNumber != NULL)
						{
							if (*m_szCallerNumber == '+')
								dwType = 145;
							else if (*m_szCallerNumber == '1')
								dwType = 161;
						}
						if (*m_szCallerName != 0)
							ConvertString(szMappedName, m_szCallerName, sizeof(szMappedName));
						PostResponse("\r\nRING");
						if (ReportELIP())
						{
							if (szMappedName[0] != 0)
								sprintf(szReply, "*ELIP: \"%s\"", szMappedName);
							PostResponse(szReply);
						}
						if (ReportCLIP())
						{
							if ((g_dwReportCIDName) && (szMappedName[0] != 0)) // Put caller name where number should be - HACK for Nokia HS-12W
								sprintf(szReply, "+CLIP: \"%s\",%i", szMappedName, dwType);
							else if (szMappedName[0] != 0)
								sprintf(szReply, "+CLIP: \"%ls\",%i,,,\"%s\"", lpNumber, dwType, szMappedName);
							else
								sprintf(szReply, "+CLIP: \"%ls\",%i", lpNumber, dwType);
							PostResponse(szReply);
						}
					}
					*/
				}
#ifdef DEBUG
				sprintf(szLog, "Notified of TalkingName change (%i) to %S", bChanged, m_szCallerName);
				LogString(szLog, DEBUG_CALLERID);
#endif
				break;
			}
			case TALKING_NUMBER:
			{
				BOOL bChanged = FALSE;
				if (m_szCallerNumber != NULL)
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						if (wcsncmp(m_szCallerNumber, (TCHAR *) pNotify->rgData, pNotify->cbData/sizeof(TCHAR)) != 0)
						{
							m_szCallerNumber = (TCHAR *) LocalReAlloc(m_szCallerNumber, pNotify->cbData, LPTR);
							bChanged = TRUE;
						}
					}
					else
					{
						LocalFree(m_szCallerNumber);
						m_szCallerNumber = NULL;
						bChanged = TRUE;
					}
				}
				else
				{
					if (pNotify->cbData >= 2 * sizeof(TCHAR))
					{
						m_szCallerNumber = (TCHAR *) LocalAlloc(LPTR, pNotify->cbData);
						bChanged = TRUE;
					}
				}
				if (m_szCallerNumber != NULL && bChanged)
				{
					memcpy(m_szCallerNumber, pNotify->rgData, pNotify->cbData);
					/*
					if (m_wCallIDActive) //either CLIP or ELIP
					{
						DWORD	dwType = 129;
						CHAR	szReply[1024];
						CHAR	szMappedName[512];
						szMappedName[0] = 0;
						
						if (m_szCallerNumber != NULL)
						{
							if (*m_szCallerNumber == '+')
								dwType = 145;
							else if (*m_szCallerNumber == '1')
								dwType = 161;
						}
						if (*m_szCallerName != 0)
							ConvertString(szMappedName, m_szCallerName, sizeof(szMappedName));
						PostResponse("\r\nRING");
						if (ReportELIP())
						{
							if (szMappedName[0] != 0)
								sprintf(szReply, "*ELIP: \"%s\"", szMappedName);
							PostResponse(szReply);
						}
						if (ReportCLIP())
						{
							if ((g_dwReportCIDName) && (szMappedName[0] != 0)) // Put caller name where number should be - HACK for Nokia HS-12W
								sprintf(szReply, "+CLIP: \"%s\",%i", szMappedName, dwType);
							else if (szMappedName[0] != 0)
								sprintf(szReply, "+CLIP: \"%ls\",%i,,,\"%s\"", lpNumber, dwType, szMappedName);
							else
								sprintf(szReply, "+CLIP: \"%ls\",%i", lpNumber, dwType);
							PostResponse(szReply);
						}
					}
					*/
#ifdef DEBUG
				sprintf(szLog, "Notified of TalkingNumber change (%i) to %S", bChanged, m_szCallerName);
				LogString(szLog, DEBUG_CALLERID);
#endif
				}
				break;
			}
#endif
#ifdef USE_NOTIFY_HF
			case HANDS_FREE:
			{
				if (pNotify->cbData >= sizeof(DWORD))
				{
					m_bHandsfree = (BOOL) (*(DWORD *) pNotify->rgData);
					// No need to act on this, we have HFPConnected and thread ID doing it
#ifdef DEBUG
					sprintf(szLog, "HandsFree changed to %i, HFP is %i, InSession is %i", m_bHandsfree, HFPConnected(), bInSession);
					LogString(szLog, DEBUG_THREADING);
#endif
				}
				else
					m_bHandsfree = FALSE;
				break;
			}
#endif
			default:
				break;
		}
	}
	delete[] pNotify;
	return TRUE;
}

BOOL CNotifyMgr::GetOperator(TCHAR * szOperator, DWORD dwSize)
{
	BOOL bRet = FALSE;
	if (m_szOperator != NULL)
	{
		wcsncpy(szOperator, m_szOperator, dwSize);
		szOperator[dwSize-1] = 0;
		bRet = TRUE;
	}
	return bRet;
}

#ifdef USE_NOTIFY_CID
BOOL CNotifyMgr::GetCallerName(TCHAR * szName, DWORD dwSize)
{
	BOOL bRet = FALSE;
	if (m_szCallerName != NULL)
	{
		wcsncpy(szName, m_szCallerName, dwSize);
		szName[dwSize-1] = 0;
		bRet = TRUE;
	}
	return bRet;
}

BOOL CNotifyMgr::GetCallerNumber(TCHAR * szNumber, DWORD dwSize)
{
	BOOL bRet = FALSE;
	if (m_szCallerNumber != NULL)
	{
		wcsncpy(szNumber, m_szCallerNumber, dwSize);
		szNumber[dwSize-1] = 0;
		bRet = TRUE;
	}
	return bRet;
}

#endif
